//
// Created by F1997 on 2025/3/4.
//

#ifndef EFVICAP_MY_EFVICAP_H
#define EFVICAP_MY_EFVICAP_H

#include <etherfabric/memreg.h>
#include <etherfabric/pd.h>
#include <etherfabric/vi.h>

#include <arpa/inet.h>
#include <net/ethernet.h>
#include <net/if.h>
#include <netinet/ip.h>
#include <netinet/tcp.h>
#include <netinet/udp.h>

#include <array>
#include <atomic>
#include <csignal>
#include <cstring>
#include <iostream>
#include <pcap.h>
#include <sys/mman.h>
#include <system_error>
#include <unistd.h>
#include <vector>
#include <chrono>
#include <bits/signum.h>
#include <stdlib.h>

#include "pch.h"
#include "IniReader.h"
#include "SafeQueueTemplate.h"
#include "../decode/shfe_decode.h"

#pragma once

// constexpr表达式是指值不会改变并且在编译过程就能得到计算结果的表达式
// Per Solarflare documentation / Solarflare文档中建议的 pktBufSize 大小
constexpr size_t pktBufSize = 2048;

// Should be multiple of 8 according to Solarflare documentation / 8的整数倍
constexpr int refillBatchSize = 16;

// Huge page size for your platform / 内存大页
#if defined(__x86_64__) || defined(__i386__)
constexpr size_t hugePageSize = 2 * 1024 * 1024;
#endif
// 重载了输出流操作符<<，用于将in_addr类型的IP地址转换为字符串并输出
static std::ostream &operator<<(std::ostream &os, const in_addr addr) {
    std::array<char, INET_ADDRSTRLEN> str = {};
    inet_ntop(AF_INET, &addr, str.data(), str.size());
    os.write(str.data(), strnlen(str.data(), str.size()));
    return os;
}

// 处理数据包
static void printPacket(const void *buf, int len, const struct pcap_pkthdr *header,
        const std::string destinationIP1, const std::string destinationIP2,
        std::ofstream &outFile1, std::ofstream &outFile2) {
    auto p = reinterpret_cast<const char *>(buf);
    auto eth = reinterpret_cast<const ether_header *>(p);
    if (ntohs(eth->ether_type) == ETHERTYPE_IP) { // ip 类型头部，ntohs=将数据从网络字节顺序转换为主机字节顺序
        auto ip = reinterpret_cast<const iphdr *>(p + sizeof(ether_header));
        if (ip->version == 4) { // 只处理 ipv4
            if (ip->protocol == IPPROTO_TCP) { // tcp 类型
/*                auto tcp = reinterpret_cast<const tcphdr *>(p + sizeof(ether_header) +
                                                            sizeof(iphdr));
                std::cout << "ip tcp from " << in_addr{ip->saddr} << ":"
                          << ntohs(tcp->source) << " to " << in_addr{ip->daddr} << ":"
                          << ntohs(tcp->dest) << std::endl;*/
            } else if (ip->protocol == IPPROTO_UDP) { // udp 类型
                auto udp = reinterpret_cast<const udphdr *>(p + sizeof(ether_header) +
                                                            sizeof(iphdr));
/*                std::cout << "ip udp from "
                          //<< get_cur_time_ns() << " "
                          << in_addr{ip->saddr} << ":"
                          << ntohs(udp->source) << " to " << in_addr{ip->daddr} << ":"
                          << ntohs(udp->dest)
                          << std::endl;*/

                // 按组播ip进行过滤，分开保存
                // string转chart*
                if (ip->daddr == inet_addr(destinationIP1.c_str())) {
                    // 解析数据并保存数据，一个线程中保存2路数据文件
                    MarketData marketData;
                    marketData.destinationIP = std::string(destinationIP1);
                    shfe_decode::parse_packet_shfe(header, reinterpret_cast<const u_char*>(buf), marketData, outFile1);
                } else if (ip->daddr == inet_addr(destinationIP2.c_str())) {
                    MarketData marketData;
                    marketData.destinationIP = std::string(destinationIP2);
                    shfe_decode::parse_packet_shfe(header, reinterpret_cast<const u_char*>(buf), marketData, outFile2);
                }

            } else { // 其他类型
                //std::cout << "ip " << ip->protocol << std::endl;
            }
        }
    } else if (ntohs(eth->ether_type) == ETHERTYPE_ARP) { // arp 类型
        //std::cout << "arp" << std::endl;
    } else {
        // 其他类型
        //std::cout << ntohs(eth->ether_type) << std::endl;
    }
}

std::atomic<bool> active = {true};

// 信号处理函数
extern "C" void signalHandler(int /*signal*/) { active = false; }


int main3(std::string sectionId, ntp::IniReader* iniCfg) {

    // 从配置文件中获取配置
    std::string device = iniCfg->ReadString(sectionId.data(), "device","");
    std::string sourceIP = iniCfg->ReadString(sectionId.data(), "sourceIP","");
    std::string destinationIP = iniCfg->ReadString(sectionId.data(), "destinationIP","");
    std::string destinationPort = iniCfg->ReadString(sectionId.data(), "destinationPort","");
    std::string filterStr = iniCfg->ReadString(sectionId.data(), "filterStr",""); // 抓包过滤条件

    std::string destinationIP1 = iniCfg->ReadString("LINE_1", "destinationIP","");
    std::string destinationIP2 = iniCfg->ReadString("LINE_2", "destinationIP","");

    std::string debug;
    std::string interface = device;
    std::string filename;
    bool hw_timestamps = true;
    bool sniff_transmit = false;
    std::cout << "参数 ===> device=" << device
    << " sourceIP=" << sourceIP
    << " destinationIP=" << destinationIP
    << " destinationPort=" << destinationPort
    << " " << filterStr << std::endl;

    // 结果文件
    std::ofstream outFile1("pcap_out_LINE_1.csv");
    std::ofstream outFile2("pcap_out_LINE_2.csv");

    // ===================================================

    std::cout << "版本要求"
              << ef_vi_version_str() << ","
              << ef_vi_driver_interface_str() << ","
              << std::endl;

    struct Filter {
        in_addr addr;
        in_port_t port;
    };
    std::vector<Filter> filters; // ip和端口过滤器
/*    for (int i = optind; i < argc; i++) { // optind —— 用来记录argv中下一个检索的位置
        Filter filter = {};
        char *sep = strchr(argv[i], ':'); // 从c风格的字符串中查找第一个字符出现的位置
        if (sep) {
            *sep = 0;
            filter.port = htons(atoi(sep + 1));
        }
        if (inet_aton(argv[i], &filter.addr) == 0) {
            std::runtime_error("invalid address");
        }
        filters.push_back(filter);
    }*/

    std::cout << "过滤器：" << std::endl;

    // 将信号处理函数注册为处理 SIGINT 信号
    std::signal(SIGINT, signalHandler);

    struct {
        // Resource handles / 资源句柄
        ef_driver_handle dh; // ef驱动程序句柄，用于与驱动程序交互
        struct ef_pd pd; // 物理设备（Physical Device）句柄，表示分配的物理设备
        struct ef_vi vi; // 虚拟接口，是一个事件队列和两个 dna 队列的集合，用于在传输实现和接口之间传递以太网帧
        int rxPrefixLen; // rx 缓冲区的前缀长度，即在每个数据包开头添加的额外字节数，用于存储数据包头信息，如时间戳等。

        // DMA memory
        void *pktBufs; // 指向DMA内存的指针，用于存储接收到的网络包。DMA内存可以直接由硬件写入，避免了CPU的频繁拷贝操作，提高了性能
        int nPktBufs; // 分配的DMA内存中缓冲区的数量，决定了可以同时处理的最大包数量
        struct ef_memreg memreg; // 已注册的 DMA 内存。存储 DMA 缓冲区的内存映射地址。通过注册内存，确保DMA控制器知道如何访问这些内存区域
        std::vector<int> freePktBufs; // 空闲的 DMA 缓冲区索引，记录当前可用的空闲缓冲区ID列表，便于在接收到新包时分配新的缓冲区
        std::vector<ef_addr> pktBufAddrs; // 每个DMA缓冲区的DMA地址列表，记录每个DMA缓冲区的DMA地址，方便硬件直接访问这些缓冲区
    } res = {};
    // res 结构体在整个程序中起到了资源管理和内存分配的核心作用

    std::cout << "资源句柄：" << std::endl;

    if (ef_driver_open(&res.dh) < 0) { // 打开驱动程序，并获取设备句柄 res.dh
        throw std::system_error(errno, std::generic_category(), "ef_driver_open");
    }
    // 分配保护域，根据指定的接口名称分配物理设备 res.pd
    if (ef_pd_alloc_by_name(&res.pd, res.dh, interface.c_str(), EF_PD_DEFAULT) <
        0) {
        throw std::system_error(errno, std::generic_category(),
                                "ef_pd_alloc_by_name");
    }
    // 设置虚拟接口的标志 vi_flags ，如果启用了硬件时间，则修改为 EF_VI_RX_TIMESTAMPS 标记
    unsigned vi_flags = EF_VI_FLAGS_DEFAULT;
    if (1) { // hw_timestamps
        vi_flags |= EF_VI_RX_TIMESTAMPS; // 设置 vi_flags 如：EF_VI_FLAGS_DEFAULT | EF_VI_TX_TIMESTAMPS = 为报文添加时间戳 | EF_VI_RX_TIMESTAMPS = 为数据包添加时间戳
        // a|=b 代表的含义为a=a|b;即把a和b做按位或(|)操作，结果赋值给a
    }
    // 从保护域中分配虚拟接口
    if (ef_vi_alloc_from_pd(&res.vi, res.dh, &res.pd, res.dh, -1, -1, 0, NULL, -1,
                            (enum ef_vi_flags) vi_flags) < 0) {
        throw std::system_error(errno, std::generic_category(),
                                "ef_vi_alloc_from_pd");
    }

    // Length of prefix before actual packet data / 实际数据包前的前缀长度
    res.rxPrefixLen = ef_vi_receive_prefix_len(&res.vi);

    // Allocate memory for DMA transfers. Try to get huge pages. / 为DMA传输分配内存
    res.nPktBufs = ef_vi_receive_capacity(&res.vi); // 获取接收缓存区的总容量
    const size_t bytesNeeded = res.nPktBufs * pktBufSize;
    // Round up to nearest huge page size / 四舍五入到最接近的大页面大小
    const size_t bytesRounded = (bytesNeeded / hugePageSize + 1) * hugePageSize;
    res.pktBufs = mmap(NULL, bytesRounded, PROT_READ | PROT_WRITE,
                       MAP_ANONYMOUS | MAP_PRIVATE | MAP_HUGETLB, -1, 0); // mmap分配大页内存
    if (res.pktBufs == MAP_FAILED) {
        std::cerr << "warning: failed to allocate hugepages for DMA buffers"
                  << std::endl;
        posix_memalign(&res.pktBufs, 4096, bytesRounded); // mmap分配大页内存失败后，用 posix_memalign 分配普通内存
    }

    // Register the memory for use with DMA / 注册内存以供DMA使用
    if (ef_memreg_alloc(&res.memreg, res.dh, &res.pd, res.dh, res.pktBufs,
                        bytesRounded) < 0) {
        throw std::system_error(errno, std::generic_category(), "ef_memreg_alloc");
    }

    // Store the DMA address for each packet buffer / 存储每个数据包缓冲区的 DMA 地址
    for (int i = 0; i < res.nPktBufs; ++i) {
        res.pktBufAddrs.push_back(ef_memreg_dma_addr(&res.memreg, i * pktBufSize)); // 存储每个数据包缓冲取的 DMA 地址
        res.freePktBufs.push_back(i); // 初始化空闲数据包缓冲区列表
    }


    // Fill the RX descriptor ring / 在网卡上申请 RX 缓存区
    while (ef_vi_receive_space(&res.vi) > 0 && !res.freePktBufs.empty()) { // 填充接收描述符环，直到没有空闲缓冲区或空间不足
        const int pktBufId = res.freePktBufs.back(); // 最后一个空闲缓冲区的索引/id
        res.freePktBufs.resize(res.freePktBufs.size() - 1);
        ef_vi_receive_init(&res.vi, res.pktBufAddrs[pktBufId], pktBufId); // 初始化 rx 缓冲区，并设置dma内存
    }
    ef_vi_receive_push(&res.vi); // 向网卡提交新的初始化 rx 描述符

    // 配置过滤器
    for (auto filter : filters) { // 匹配多播，遍历过滤器列表，根据端口号配置多播或单播过滤器
        // Match multicast / 匹配多播
        ef_filter_spec filter_spec; // 过滤器
        ef_filter_spec_init(&filter_spec, EF_FILTER_FLAG_NONE); // 设置过滤器后，才会接收报文
        if (filter.port == 0) {
            uint8_t mac[6] = {0x01,
                              0x00,
                              0x5e,
                              uint8_t(filter.addr.s_addr >> 8 & 0x7f),
                              uint8_t(filter.addr.s_addr >> 16 & 0xff),
                              uint8_t(filter.addr.s_addr >> 24 & 0xff)};
            if (ef_filter_spec_set_eth_local(&filter_spec, EF_FILTER_VLAN_ID_ANY, // 设置以太网的mac过滤器
                                             mac) < 0) {
                throw std::system_error(errno, std::generic_category(),
                                        "ef_filter_spec_set_port_sniff");
            }
        } else {
            if (ef_filter_spec_set_ip4_local(&filter_spec, IPPROTO_UDP, // ipv4 local 过滤器
                                             filter.addr.s_addr, filter.port) < 0) {
                throw std::system_error(errno, std::generic_category(),
                                        "ef_filter_spec_set_port_sniff");
            }
        }
        if (ef_vi_filter_add(&res.vi, res.dh, &filter_spec, NULL) < 0) { // 为虚拟接口添加过滤器
            throw std::system_error(errno, std::generic_category(),
                                    "ef_vi_filter_add");
        }
    }
    if (filters.empty()) {
        // Match all packets that also match another filter / 如果没有过滤器，设置匹配所有数据包的过滤器
        ef_filter_spec filter_spec;
        ef_filter_spec_init(&filter_spec, EF_FILTER_FLAG_NONE);
        if (ef_filter_spec_set_port_sniff(&filter_spec, 1) < 0) {
            throw std::system_error(errno, std::generic_category(),
                                    "ef_filter_spec_set_port_sniff");
        }
        if (ef_vi_filter_add(&res.vi, res.dh, &filter_spec, NULL) < 0) { // 为虚拟接口添加过滤器
            throw std::system_error(errno, std::generic_category(),
                                    "ef_vi_filter_add");
        }
    }

    if (sniff_transmit) { // 如果需要捕获发送的数据包，配置相应的发送端口过滤器
        ef_filter_spec filter_spec;
        ef_filter_spec_init(&filter_spec, EF_FILTER_FLAG_NONE);
        if (ef_filter_spec_set_tx_port_sniff(&filter_spec)) {
            throw std::system_error(errno, std::generic_category(),
                                    "ef_filter_spec_set_tx_port_sniff");
        }
        if (ef_vi_filter_add(&res.vi, res.dh, &filter_spec, NULL) < 0) {
            throw std::system_error(errno, std::generic_category(),
                                    "ef_filter_spec_set_tx_port_sniff/ef_vi_filter_add");
        }
    }

    // Open pcap output / 打开pcap输出文件，用于保存捕获的数据包
    pcap_t *pcap = nullptr;
    pcap_dumper_t *pcapDumper = nullptr;
    if (!filename.empty()) { // 如果指定了输出文件，则打开输出文件
        int link_type = DLT_EN10MB; // 链路类型
        int snap_len = 65535; // 捕获长度
        u_int precision = PCAP_TSTAMP_PRECISION_MICRO; // 时间戳精度，默认为微秒，如果启用了硬件时间戳则为纳秒
        if (hw_timestamps) {
            precision = PCAP_TSTAMP_PRECISION_NANO; // 启用硬件时间戳
        }
        pcap = pcap_open_dead_with_tstamp_precision(link_type, snap_len, precision); // 创建一个 pcap 会话
        pcapDumper = pcap_dump_open(pcap, filename.c_str()); // 打开一个 pcap 文件
        if (!pcapDumper) {
            throw std::runtime_error("pcap_dump_open: " +
                                     std::string(pcap_geterr(pcap)));
        }
    }

    std::array<ef_event, 32> evs = {}; // 用于存储从事件队列中获取的事件

    //std::cout << "===>开始捕获数据" << std::endl;
    while (active.load(std::memory_order_acquire)) {

        // evq=要轮询的事件队列，evs=返回轮询事件的数组，evs_len=数组的长度，必须 > EF_VI_EVENT_POLL_MIN_EVS
        // std::array 提供了 data() 成员函数，该函数返回指向数组第一个元素的指针
        const int nev = ef_eventq_poll(&res.vi, evs.data(), evs.size()); // 轮询事件/数据包队列
        //std::cout << "nev=" << nev << std::endl;
        if (nev > 0) { // 遍历每个 nev/事件
            for (int i = 0; i < nev; ++i) {
                switch (EF_EVENT_TYPE(evs[i])) { // 处理接收事件
                    case EF_EVENT_TYPE_RX: { // 接收到了数据
                        res.freePktBufs.push_back(EF_EVENT_RX_RQ_ID(evs[i])); // 将接收队列ID添加到 freePktBufs 中
                        if (EF_EVENT_RX_SOP(evs[i]) == 0 || EF_EVENT_RX_CONT(evs[i]) != 0) { // 检查是否为巨型帧
                            // Ignore jumbo packets
                            break;
                        }
                        const char *buf = (const char *)res.pktBufs +
                                          pktBufSize * EF_EVENT_RX_RQ_ID(evs[i]) +
                                          res.rxPrefixLen;
                        const int bufLen = EF_EVENT_RX_BYTES(evs[i]) - res.rxPrefixLen;
                        if (pcapDumper) { // 有 pcap 输出文件
                            pcap_pkthdr pktHdr = {};
                            pktHdr.caplen = bufLen;
                            pktHdr.len = bufLen;
                            if ( hw_timestamps ) { // 设置硬件时间戳，有文件
                                timespec ts; // utc时间戳
                                unsigned flags; // 时钟同步状态标记，返回值：EF_VI_SYNC_FLAG_CLOCK_SET/1 = 与系统时钟同步  EF_VI_SYNC_FLAG_CLOCK_IN_SYNC/2 与外部ptp时钟同步
                                const void *pkt = (const char *) res.pktBufs + pktBufSize * EF_EVENT_RX_RQ_ID(evs[i]); // 接收到的数据包的第一个数据包缓冲区
                                // 8 + 8 位
                                if ( ef_vi_receive_get_timestamp_with_sync_flags(&res.vi, pkt, &ts, &flags) ) { // 获取硬件时间，0为成功，非0为失败
                                    throw std::runtime_error("failed to get hw timestamp");
                                }
                                int utcOffset = 8;
                                // 对获取的时间 ts 进行格式化
                                std::cout << "ts: " << ts.tv_sec << " " << ts.tv_nsec << " " << flags << std::endl;
                                pktHdr.ts.tv_sec = ts.tv_sec * 100; // 31位秒级时间戳，转为 utc 时间 8位
                                //pktHdr.ts.tv_sec = std::chrono::high_resolution_clock::now().time_since_epoch().count(); // 转为 utc 时间，utc 纳秒时间转为本地时间
                                pktHdr.ts.tv_usec = ts.tv_nsec; // magic number should be set for nanos / 为纳秒时间戳单独设置数字类型。9位纳秒部分
                            } else {
                                // 10 + 6 位
                                //gettimeofday(&pktHdr.ts, nullptr); // 不使用硬件时间，则使用 gettimeofday 获取系统时间戳
                                //get_cur_time_ns_with_param( static_cast<timeval *>(&pktHdr.ts) ); // static_cast<timespec *>(&ts2)
                            }
                            // 将数据包写入 pcap 文件中
                            pcap_dump(reinterpret_cast<u_char *>(pcapDumper), &pktHdr,
                                      reinterpret_cast<const u_char *>(buf));
                        } else { // 没有 pcap 文件，则用 printPacket 函数打印数据包
                            struct pcap_pkthdr *header;
                            header = (pcap_pkthdr*)malloc(sizeof(pcap_pkthdr));
                            if (header != nullptr) {
                                header->caplen = static_cast<bpf_u_int32>(bufLen);
                                header->len = static_cast<bpf_u_int32>(bufLen);
                            } else {
                                throw std::runtime_error("header is null");
                            }
                            if ( hw_timestamps ) { // 设置硬件时间戳，有文件
                                timespec ts; // utc时间戳
                                unsigned flags = EF_VI_SYNC_FLAG_CLOCK_IN_SYNC; // 时钟同步状态标记，返回值：EF_VI_SYNC_FLAG_CLOCK_SET/1 = 与系统时钟同步  EF_VI_SYNC_FLAG_CLOCK_IN_SYNC/2 与外部ptp时钟同步
                                const void *pkt = (const char *) res.pktBufs + pktBufSize * EF_EVENT_RX_RQ_ID(evs[i]); // 接收到的数据包的第一个数据包缓冲区
                                // 8 + 8 位
                                //std::cout << "&res.vi=" << res.vi.dh << ",pkt=" << pkt << ",ts=" << ts.tv_sec << ",flags=" << flags << std::endl;
                                auto aa = ef_vi_receive_get_timestamp_with_sync_flags(&res.vi, pkt, &ts, &flags);
                                if ( /*ef_vi_receive_get_timestamp_with_sync_flags(&res.vi, pkt, &ts, &flags)*/ aa) { // 获取硬件时间，0为成功，非0为失败
                                    throw std::runtime_error("failed to get hw timestamp");
                                }
                                int utcOffset = 8;
                                // 对获取的时间 ts 进行格式化
                                //std::cout << "ts: " << ts.tv_sec << " " << ts.tv_nsec << " " << flags << std::endl;
                                header->ts.tv_sec = ts.tv_sec; // 31位秒级时间戳，转为 utc 时间 8位
                                //header->ts.tv_sec = std::chrono::high_resolution_clock::now().time_since_epoch().count(); // 转为 utc 时间，utc 纳秒时间转为本地时间
                                header->ts.tv_usec = ts.tv_nsec; // magic number should be set for nanos / 为纳秒时间戳单独设置数字类型。9位纳秒部分
                            } else {
                                // 10 + 6 位
                            }
                            //printPacket(buf, bufLen);
                            printPacket(buf, bufLen, header, destinationIP1, destinationIP2, outFile1, outFile2);
                        }
                        break;
                    }
                    default:
                        // 运行一段时间后会执行到这里？？？
                        //throw std::runtime_error("ef_eventq_poll: unknown event type");
                        break;
                }
            }
            // Refill the RX descriptor ring / 重新填充RX描述符环
            if (ef_vi_receive_space(&res.vi) > refillBatchSize &&
                res.freePktBufs.size() > refillBatchSize) { // 如果RX描述符环有足够的空间且有足够多的空闲缓冲区
                for (int i = 0; i < refillBatchSize; ++i) {
                    const int pkt_buf_id =
                            res.freePktBufs[res.freePktBufs.size() - refillBatchSize + i];
                    ef_vi_receive_init(&res.vi, res.pktBufAddrs[pkt_buf_id], pkt_buf_id);
                }
                res.freePktBufs.resize(res.freePktBufs.size() - refillBatchSize);
                ef_vi_receive_push(&res.vi);
            }
        }

        //sleep(3);
    }

    if (pcapDumper) {
        pcap_dump_close(pcapDumper);
        pcap_close(pcap);
    }

    return 0;
}

#endif //EFVICAP_MY_EFVICAP_H
